package com.morningstar.qs.api.basic.common.date;

import java.io.File;
import java.io.FileFilter;
import java.io.FilenameFilter;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashMap;
import java.util.List;


import org.apache.log4j.Logger;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

import com.morningstar.qs.api.basic.common.config.GlobalConfig;
import com.morningstar.qs.api.basic.util.StringUtil;
import com.morningstar.qs.api.basic.util.XMLFileUtilExt;


public class Holidays {
	private final static String DEFAULT_FILE_DIR = "D:/SVN/oneteam/QS/src/WebService/WebServiceAPI/holidays";
	private final static String DEFAULT_FILE_DIR_ENDING = "/"; 
	private final String DEFAULT_EXTEND_NAME = ".xml";
	
	private static Logger gLogger = Logger.getLogger(Holidays.class);
	private static Holidays holidays = new Holidays();
	
	private String fileDir = null;
	private HashMap<String, List<Date>> holidaysData = null;
	private HashMap<String, List<Long>> holidaysDiffDays = null;
	
	private Holidays(){
		String configHolidayPath = GlobalConfig.getInstance().getProperty("holiday_xml_path");
		if (null == configHolidayPath || configHolidayPath.isEmpty())
		{
			this.fileDir = DEFAULT_FILE_DIR;
		}
		else
		{
			this.fileDir = configHolidayPath;
		}
		
		//loadConfig();
		fillHolidaysData();
		fillHolidaysDiffDays();
	}
	
	public static synchronized Holidays getInstance() {
		return holidays;
	}
	
//	private boolean loadConfig(){		
//		Properties prop = GlobalConfig.getInstance().loadConfigProperties();
//		if (null == prop) {
//			gLogger.error("load qsweb.properties unsuccessfully");
//			return false;
//		}
//		String path = prop.getProperty("holiday_xml_path");
//		if (null == path || 0 == path.length()) {
//			this.fileDir = DEFAULT_FILE_DIR;
//			gLogger.info("load the holiday xml path of qsweb.properties unsuccessfully");
//		} else {
//			this.fileDir = path;
//		}
//		return true;
//	}
	
	public boolean fillHolidaysData() {
		String dir = StringUtil.formatDirPath(this.fileDir, DEFAULT_FILE_DIR_ENDING);
		File[] files = HolidaysFileFilter.getFiles(dir, DEFAULT_EXTEND_NAME);
		boolean ret = false;
		
		if (null == files || 0 == files.length) {
			return ret;
		}
		
		this.holidaysData = new HashMap<String, List<Date>>();
		String fullPath, fullName, name;
		for (int i = 0; i < files.length; i++) {
			fullPath = files[i].toString();
			fullName = files[i].getName();
			if (null == fullPath || null == fullName) {
				continue;
			}
			int idx = fullName.indexOf(".");
			name = fullName.substring(0, idx);
			if (null == name || 0 == name.length()) {
				continue;
			}
			List<Date> lstDate = getDateList(fullPath);
			if (null == lstDate) {
				continue;
			}
			this.holidaysData.put(name, lstDate);
		}
		
		return ret;
	}
	
	public boolean fillHolidaysDiffDays() {
		String dir = StringUtil.formatDirPath(this.fileDir, DEFAULT_FILE_DIR_ENDING);
		File[] files = HolidaysFileFilter.getFiles(dir, DEFAULT_EXTEND_NAME);
		boolean ret = false;
		
		if (null == files || 0 == files.length) {
			return ret;
		}
		
		this.holidaysDiffDays = new HashMap<String, List<Long>>();
		String fullPath, fullName, name;
		for (int i = 0; i < files.length; i++) {
			fullPath = files[i].toString();
			fullName = files[i].getName();
			if (null == fullPath || null == fullName) {
				continue;
			}
			int idx = fullName.indexOf(".");
			name = fullName.substring(0, idx);
			if (null == name || 0 == name.length()) {
				continue;
			}
			List<Long> lstDiffDays = getDiffDaysList(fullPath);
			if (null == lstDiffDays) {
				continue;
			}
			this.holidaysDiffDays.put(name, lstDiffDays);
		}
		
		return ret;
	}
	
	public List<Date> getDateList(String fileName) {
		if (null == fileName || 0 == fileName.length()) {
			return null;
		}
		
		List<Date> lstDate = new ArrayList<Date>();
		try {
			org.w3c.dom.Document doc = null;
			doc = XMLFileUtilExt.XMLReader(fileName);
			Element emRoot = doc.getDocumentElement();
			emRoot.normalize();

			NodeList lstHolidays = emRoot.getElementsByTagName("MarketHolidays");
			if (null != lstHolidays && 0 < lstHolidays.getLength()) {
				for (int i = 0; i < lstHolidays.getLength(); i++) {
					Element emHoliday = (Element) lstHolidays.item(i);
					NodeList lstDays = emHoliday.getElementsByTagName("MarketHoliday");
					for (int j = 0; j < lstDays.getLength(); j++) {
						Element emDay = (Element) lstDays.item(j);
						NodeList day = emDay.getElementsByTagName("Date");
						if (null != day && 0 < day.getLength()) {
							Element emDate = (Element) day.item(0);
							String text = emDate.getTextContent();
							if (null != text) {
								Date date = new SimpleDateFormat("yyyy-MM-dd").parse(text);
								if (null != date) {
									lstDate.add(date);
								}
							}
						}
					}
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
			gLogger.error("Failed to read the holidays from the file " + fileName + " .", e);
		}
		return lstDate;
	}
	
	public List<Long> getDiffDaysList(String fileName) {
		if (null == fileName || 0 == fileName.length()) {
			return null;
		}
		
		List<Long> lstDiffDays = new ArrayList<Long>();
		try {
			org.w3c.dom.Document doc = null;
			doc = XMLFileUtilExt.XMLReader(fileName);
			Element emRoot = doc.getDocumentElement();
			emRoot.normalize();

			NodeList lstHolidays = emRoot.getElementsByTagName("MarketHolidays");
			if (null != lstHolidays && 0 < lstHolidays.getLength()) {
				for (int i = 0; i < lstHolidays.getLength(); i++) {
					Element emHoliday = (Element) lstHolidays.item(i);
					NodeList lstDays = emHoliday.getElementsByTagName("MarketHoliday");
					for (int j = 0; j < lstDays.getLength(); j++) {
						Element emDay = (Element) lstDays.item(j);
						NodeList day = emDay.getElementsByTagName("Date");
						if (null != day && 0 < day.getLength()) {
							Element emDate = (Element) day.item(0);
							String text = emDate.getTextContent();
							if (null != text) {
								Date date = new SimpleDateFormat("yyyy-MM-dd").parse(text);
								if (null != date) {
									long diffDays = DateFrequencyProcess.getDaysFromOrigin(date);
									if (0 <= diffDays) {
										lstDiffDays.add(diffDays);	
									}
								}
							}
						}
					}
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
			gLogger.error("Failed to read the holidays from the file " + fileName + " .", e);
		}
		return lstDiffDays;
	}
	
	public boolean isHolidayByDate(String country, Date date) {
		if (null == this.holidaysData || null == country || 0 == country.length() || null == date) {
			return false;
		}
		boolean ret = false;
		List<Date> holidays = this.holidaysData.get(country);
		if (null == holidays) {
			return ret;
		}
		ret = holidays.contains(date);
		return ret;
	}
	
	public boolean isHolidayByDiffDays(String country, long diffDays) {
		if (null == this.holidaysDiffDays || null == country || 0 == country.length() || 0 >= diffDays) {
			return false;
		}
		
		boolean ret = false;
		List<Long> holidays = this.holidaysDiffDays.get(country);
		if (null == holidays) {
			return ret;
		}
		ret = holidays.contains(diffDays);
		return ret;
	}

	public void setHolidaysData(HashMap<String, List<Date>> holidaysData) {
		this.holidaysData = holidaysData;
	}

	public HashMap<String, List<Date>> getHolidaysData() {
		return holidaysData;
	}

	public void setFileDir(String fileDir) {
		this.fileDir = fileDir;
	}

	public String getFileDir() {
		return fileDir;
	}

	public void setHolidaysDiffDays(HashMap<String, List<Long>> holidaysDiffDays) {
		this.holidaysDiffDays = holidaysDiffDays;
	}

	public HashMap<String, List<Long>> getHolidaysDiffDays() {
		return holidaysDiffDays;
	}
}

class HolidaysFileFilter{
	private static Logger gLogger = Logger.getLogger(HolidaysFileFilter.class);
	
	public static File[] getFiles(String dirPath, String extension) {
		File[] files = null;
		try {
			File dir = new File(dirPath);
			if (!dir.exists()) {
				gLogger.error("The directory path " + dirPath + " is not existed.");
			}
			if (dir.isDirectory()) {
				files = dir.listFiles(getExtensionFileFilter(extension));
			}
		} catch (Exception e) {
			e.printStackTrace();
			gLogger.error("", e);
		}
		return files;
	}
	
	public static List<String> getFilesByRecursion(String dirPath, String extension) {
		List<String> files = new ArrayList<String>();
		try {
			File dir = new File(dirPath);
			if (!dir.exists()) {
				gLogger.error("The directory path " + dirPath + " is not existed.");
			}
			if (dir.isDirectory()) {
				File[] list = dir.listFiles(getExtensionFileFilter(extension));
				for (int i = 0; i < list.length; i++) {
					files.add(list[i].toString());
				}
				list = dir.listFiles(getDirFileFilter());
				for (int i = 0; i < list.length; i++) {
					if (list[i].isDirectory()) {
						getFilesByRecursion(list[i].toString(), extension);	
					}	
				}
			}
		} catch (Exception e) {
			e.printStackTrace();
			gLogger.error("", e);
		}
		return files;
	}
	
	public static FilenameFilter getExtensionFileFilter(String extension) {
		final String ext = extension;
		return new FilenameFilter() {
			public boolean accept(File file, String name) {
				boolean ret = name.endsWith(ext);
				return ret;
			}
		};
	}
	
	public static FileFilter getDirFileFilter() {
		return new FileFilter() {
			public boolean accept(File file) {
				return file.isDirectory();
			}
		};
	}
}
